Skip to content

Conversation

@Michael137
Copy link
Member

@Michael137 Michael137 commented Oct 24, 2025

In the past we used to only mark variables artificial that were isImplicit. We would also omit the location information if the variable's parent was implicit. Since #100355 we made the logic to mark variables as artificial the same as determining whether to omit its location or not. This was to support binding variable declarations, which we would like to have line information for (and don't want to mark artificial as they are explicitly named in source).

However, this doesn't quite do the expected for parameters of Objective-C synthesised property accessors. An Objective-C setter will have an explicit parameter, which is the ivar to write to. However, because the parent (i.e., the synthesised method) is artificial, we now mark that parameter artificial. This is example debug-info for such an accessor:

0x00000118:   DW_TAG_subprogram                                                                           
                DW_AT_low_pc    (0x0000000000000044)
                DW_AT_high_pc   (0x0000000000000078)
                DW_AT_frame_base        (DW_OP_reg29 W29)   
                DW_AT_object_pointer    (0x00000128)
                DW_AT_specification     (0x00000068 "-[Foo setFooProp:]")
                                                     
0x00000128:     DW_TAG_formal_parameter
                  DW_AT_location        (DW_OP_fbreg -8)
                  DW_AT_name    ("self")                                                                  
                  DW_AT_type    (0x00000186 "Foo *")
                  DW_AT_artificial      (true)    
                                                     
0x00000131:     DW_TAG_formal_parameter
                  DW_AT_location        (DW_OP_breg31 WSP+16)
                  DW_AT_name    ("_cmd")
                  DW_AT_type    (0x0000018b "SEL")
                  DW_AT_artificial      (true)      
                                                     
0x0000013a:     DW_TAG_formal_parameter                                                                   
                  DW_AT_location        (DW_OP_breg31 WSP+8)
                  DW_AT_name    ("fooProp")                                                               
                  DW_AT_type    (0x000000aa "id")
                  DW_AT_artificial      (true)    

Note how the fooProp parameter is marked artificial, although it technically is an explicitly passed parameter. We want to treat the synthesised method like any other, where explicitly passed parameters aren't artificial. But we do want to omit the file/line info because it doesn't exist in the source.

This patch prevents such parameters from being marked artificial. We could probably generalise this to any kind of synthesised method, not just Objective-C. But I'm currently not aware of such synthesised functions, so made it Objective-C specific for now for testability.

Motivator
Marking such parameters artificial makes LLDB fail to parse the ObjC method and emit an error such as:

error: Foo.o [0x00000000000009d7]: invalid Objective-C method DW_TAG_subprogram (DW_TAG_subprogram), please file a bug and attach the file at the start of this error message

rdar://163063569

@Michael137 Michael137 changed the title [clang][DebugInfo] Don't mark parameters of synthesized ObjC property accessors artificial [clang][DebugInfo] Don't mark explicit parameter of synthesized ObjC property accessors artificial Oct 24, 2025
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:codegen IR generation bugs: mangling, exceptions, etc. debuginfo labels Oct 24, 2025
@llvmbot
Copy link
Member

llvmbot commented Oct 24, 2025

@llvm/pr-subscribers-clang-codegen

Author: Michael Buch (Michael137)

Changes

In the past we used to only mark variables artificial that were isImplicit. We would also omit the location information if the variable's parent was implicit. Since #100355 we made the logic to mark variables as artificial the same as determining whether to omit its location or not. This was to support binding variable declarations, which we would like to have line information for (and don't want to mark artificial as they are explicitly named in source).

However, this doesn't quite do the expected for parameters of Objective-C synthesised property accessors. An Objective-C setter will have an explicit parameter, which is the ivar to write to. However, because the parent (i.e., the synthesised method) is artificial, we now mark that parameter artificial. This is example debug-info for such an accessor:

0x00000118:   DW_TAG_subprogram                                                                           
                DW_AT_low_pc    (0x0000000000000044)
                DW_AT_high_pc   (0x0000000000000078)
                DW_AT_frame_base        (DW_OP_reg29 W29)   
                DW_AT_object_pointer    (0x00000128)
                DW_AT_specification     (0x00000068 "-[Foo setFooProp:]")
                                                     
0x00000128:     DW_TAG_formal_parameter
                  DW_AT_location        (DW_OP_fbreg -8)
                  DW_AT_name    ("self")                                                                  
                  DW_AT_type    (0x00000186 "Foo *")
                  DW_AT_artificial      (true)    
                                                     
0x00000131:     DW_TAG_formal_parameter
                  DW_AT_location        (DW_OP_breg31 WSP+16)
                  DW_AT_name    ("_cmd")
                  DW_AT_type    (0x0000018b "SEL")
                  DW_AT_artificial      (true)      
                                                     
0x0000013a:     DW_TAG_formal_parameter                                                                   
                  DW_AT_location        (DW_OP_breg31 WSP+8)
                  DW_AT_name    ("fooProp")                                                               
                  DW_AT_type    (0x000000aa "id")
                  DW_AT_artificial      (true)    

Note how the fooProp parameter is marked artificial, although it really isn't. We want to treat the synthesised method like any other, where explicitly passed parameters aren't artificial.

This patch prevents such parameters from being marked artificial. We could probably generalise this to any kind of synthesised method, not just Objective-C. But I'm currently not aware of such synthesised functions, so made it Objective-C specific for now for testability.

rdar://163063569


Full diff: https://github.com/llvm/llvm-project/pull/164998.diff

2 Files Affected:

  • (modified) clang/lib/CodeGen/CGDebugInfo.cpp (+33-1)
  • (added) clang/test/DebugInfo/ObjC/property-synthesized-accessors.m (+63)
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 12e2813ef2ec7..6af806686a3b9 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -110,6 +110,33 @@ static bool IsArtificial(VarDecl const *VD) {
                               cast<Decl>(VD->getDeclContext())->isImplicit());
 }
 
+/// Returns \c true if the specified variable \c VD is an explicit parameter of
+/// a synthesized Objective-C property accessor. E.g., a synthesized property
+/// setter method will have a single explicit parameter which is the property to
+/// set.
+static bool IsObjCSynthesizedPropertyExplicitParameter(VarDecl const *VD) {
+  assert(VD);
+
+  if (!llvm::isa<ParmVarDecl>(VD))
+    return false;
+
+  // Not a property method.
+  const auto *Method =
+      llvm::dyn_cast_or_null<ObjCMethodDecl>(VD->getDeclContext());
+  if (!Method)
+    return false;
+
+  // Not a synthesized property accessor.
+  if (!Method->isImplicit() || !Method->isPropertyAccessor())
+    return false;
+
+  // Not an explicit parameter.
+  if (VD->isImplicit())
+    return false;
+
+  return true;
+}
+
 CGDebugInfo::CGDebugInfo(CodeGenModule &CGM)
     : CGM(CGM), DebugKind(CGM.getCodeGenOpts().getDebugInfo()),
       DebugTypeExtRefs(CGM.getCodeGenOpts().DebugTypeExtRefs),
@@ -5158,7 +5185,12 @@ llvm::DILocalVariable *CGDebugInfo::EmitDeclare(const VarDecl *VD,
   }
   SmallVector<uint64_t, 13> Expr;
   llvm::DINode::DIFlags Flags = llvm::DINode::FlagZero;
-  if (VarIsArtificial)
+
+  // While synthesized Objective-C property setters are "artificial" (i.e., they
+  // are not spelled out in source), we want to pretend they are just like a
+  // regular non-compiler generated method. Hence, don't mark explicitly passed
+  // parameters of such methods as artificial.
+  if (VarIsArtificial && !IsObjCSynthesizedPropertyExplicitParameter(VD))
     Flags |= llvm::DINode::FlagArtificial;
 
   auto Align = getDeclAlignIfRequired(VD, CGM.getContext());
diff --git a/clang/test/DebugInfo/ObjC/property-synthesized-accessors.m b/clang/test/DebugInfo/ObjC/property-synthesized-accessors.m
new file mode 100644
index 0000000000000..6ed2885024c48
--- /dev/null
+++ b/clang/test/DebugInfo/ObjC/property-synthesized-accessors.m
@@ -0,0 +1,63 @@
+// Test that synthesized accessors get treated like regular method declarations/defeinitions.
+// I.e.:
+// 1. explicitly passed parameter are not marked artificial.
+// 2. Each property accessor has a method declaration and definition.
+
+// RUN: %clang_cc1 -triple %itanium_abi_triple -emit-llvm -dwarf-version=5 -debug-info-kind=limited %s -o - | FileCheck %s --implicit-check-not "DIFlagArtificial"
+
+@interface Foo
+@property int p1;
+@end
+
+@implementation Foo
+@end
+
+int main(void) {
+  Foo *f;
+  f.p1 = 2;
+  return f.p1;
+}
+
+// CHECK: ![[P1_TYPE:[0-9]+]] = !DIBasicType(name: "int"
+// CHECK: ![[GETTER_DECL:[0-9]+]] = !DISubprogram(name: "-[Foo p1]"
+// CHECK-SAME:                                    type: ![[GETTER_TYPE:[0-9]+]]
+// CHECK-SAME:                                    flags: DIFlagArtificial | DIFlagPrototyped
+// CHECK-SAME:                                    spFlags: DISPFlagLocalToUnit)
+
+// CHECK: ![[GETTER_TYPE]] = !DISubroutineType(types: ![[GETTER_PARAMS:[0-9]+]])
+// CHECK: ![[GETTER_PARAMS]] = !{![[P1_TYPE]], ![[ID_TYPE:[0-9]+]], ![[SEL_TYPE:[0-9]+]]}
+// CHECK: ![[ID_TYPE]] = !DIDerivedType(tag: DW_TAG_pointer_type
+// CHECK-SAME:                          flags: DIFlagArtificial | DIFlagObjectPointer)
+// CHECK: ![[SEL_TYPE]] = !DIDerivedType(tag: DW_TAG_typedef, name: "SEL"
+// CHECK-SAME:                           flags: DIFlagArtificial)
+
+// CHECK: ![[SETTER_DECL:[0-9]+]] = !DISubprogram(name: "-[Foo setP1:]"
+// CHECK-SAME:                                    type: ![[SETTER_TYPE:[0-9]+]]
+// CHECK-SAME:                                    flags: DIFlagArtificial | DIFlagPrototyped
+// CHECK-SAME:                                    spFlags: DISPFlagLocalToUnit)
+// CHECK: ![[SETTER_TYPE]] = !DISubroutineType(types: ![[SETTER_PARAMS:[0-9]+]])
+// CHECK: ![[SETTER_PARAMS]] = !{null, ![[ID_TYPE]], ![[SEL_TYPE]], ![[P1_TYPE]]}
+
+// CHECK: ![[GETTER_DEF:[0-9]+]] = distinct !DISubprogram(name: "-[Foo p1]"
+// CHECK-SAME:                                            type: ![[GETTER_TYPE]]
+// CHECK-SAME:                                            flags: DIFlagArtificial | DIFlagPrototyped
+// CHECK-SAME:                                            spFlags: DISPFlagLocalToUnit | DISPFlagDefinition
+// CHECK-SAME:                                            declaration: ![[GETTER_DECL]]
+
+// CHECK: !DILocalVariable(name: "self", arg: 1, scope: ![[GETTER_DEF]]
+// CHECK-SAME:             flags: DIFlagArtificial | DIFlagObjectPointer)
+//
+// CHECK: !DILocalVariable(name: "_cmd", arg: 2, scope: ![[GETTER_DEF]],
+// CHECK-SAME:             flags: DIFlagArtificial)
+
+// CHECK: ![[SETTER_DEF:[0-9]+]] = distinct !DISubprogram(name: "-[Foo setP1:]",
+// CHECK-SAME:                                            type: ![[SETTER_TYPE]]
+// CHECK-SAME:                                            flags: DIFlagArtificial | DIFlagPrototyped
+// CHECK-SAME:                                            spFlags: DISPFlagLocalToUnit | DISPFlagDefinition
+// CHECK-SAME:                                            declaration: ![[SETTER_DECL]]
+
+// CHECK: !DILocalVariable(name: "self", arg: 1, scope: ![[SETTER_DEF]]
+// CHECK-SAME:             flags: DIFlagArtificial | DIFlagObjectPointer
+// CHECK: !DILocalVariable(name: "_cmd", arg: 2, scope: ![[SETTER_DEF]]
+// CHECK-SAME:             flags: DIFlagArtificial
+// CHECK: !DILocalVariable(name: "p1", arg: 3, scope: ![[SETTER_DEF]]

@llvmbot
Copy link
Member

llvmbot commented Oct 24, 2025

@llvm/pr-subscribers-debuginfo

Author: Michael Buch (Michael137)

Changes

In the past we used to only mark variables artificial that were isImplicit. We would also omit the location information if the variable's parent was implicit. Since #100355 we made the logic to mark variables as artificial the same as determining whether to omit its location or not. This was to support binding variable declarations, which we would like to have line information for (and don't want to mark artificial as they are explicitly named in source).

However, this doesn't quite do the expected for parameters of Objective-C synthesised property accessors. An Objective-C setter will have an explicit parameter, which is the ivar to write to. However, because the parent (i.e., the synthesised method) is artificial, we now mark that parameter artificial. This is example debug-info for such an accessor:

0x00000118:   DW_TAG_subprogram                                                                           
                DW_AT_low_pc    (0x0000000000000044)
                DW_AT_high_pc   (0x0000000000000078)
                DW_AT_frame_base        (DW_OP_reg29 W29)   
                DW_AT_object_pointer    (0x00000128)
                DW_AT_specification     (0x00000068 "-[Foo setFooProp:]")
                                                     
0x00000128:     DW_TAG_formal_parameter
                  DW_AT_location        (DW_OP_fbreg -8)
                  DW_AT_name    ("self")                                                                  
                  DW_AT_type    (0x00000186 "Foo *")
                  DW_AT_artificial      (true)    
                                                     
0x00000131:     DW_TAG_formal_parameter
                  DW_AT_location        (DW_OP_breg31 WSP+16)
                  DW_AT_name    ("_cmd")
                  DW_AT_type    (0x0000018b "SEL")
                  DW_AT_artificial      (true)      
                                                     
0x0000013a:     DW_TAG_formal_parameter                                                                   
                  DW_AT_location        (DW_OP_breg31 WSP+8)
                  DW_AT_name    ("fooProp")                                                               
                  DW_AT_type    (0x000000aa "id")
                  DW_AT_artificial      (true)    

Note how the fooProp parameter is marked artificial, although it really isn't. We want to treat the synthesised method like any other, where explicitly passed parameters aren't artificial.

This patch prevents such parameters from being marked artificial. We could probably generalise this to any kind of synthesised method, not just Objective-C. But I'm currently not aware of such synthesised functions, so made it Objective-C specific for now for testability.

rdar://163063569


Full diff: https://github.com/llvm/llvm-project/pull/164998.diff

2 Files Affected:

  • (modified) clang/lib/CodeGen/CGDebugInfo.cpp (+33-1)
  • (added) clang/test/DebugInfo/ObjC/property-synthesized-accessors.m (+63)
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 12e2813ef2ec7..6af806686a3b9 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -110,6 +110,33 @@ static bool IsArtificial(VarDecl const *VD) {
                               cast<Decl>(VD->getDeclContext())->isImplicit());
 }
 
+/// Returns \c true if the specified variable \c VD is an explicit parameter of
+/// a synthesized Objective-C property accessor. E.g., a synthesized property
+/// setter method will have a single explicit parameter which is the property to
+/// set.
+static bool IsObjCSynthesizedPropertyExplicitParameter(VarDecl const *VD) {
+  assert(VD);
+
+  if (!llvm::isa<ParmVarDecl>(VD))
+    return false;
+
+  // Not a property method.
+  const auto *Method =
+      llvm::dyn_cast_or_null<ObjCMethodDecl>(VD->getDeclContext());
+  if (!Method)
+    return false;
+
+  // Not a synthesized property accessor.
+  if (!Method->isImplicit() || !Method->isPropertyAccessor())
+    return false;
+
+  // Not an explicit parameter.
+  if (VD->isImplicit())
+    return false;
+
+  return true;
+}
+
 CGDebugInfo::CGDebugInfo(CodeGenModule &CGM)
     : CGM(CGM), DebugKind(CGM.getCodeGenOpts().getDebugInfo()),
       DebugTypeExtRefs(CGM.getCodeGenOpts().DebugTypeExtRefs),
@@ -5158,7 +5185,12 @@ llvm::DILocalVariable *CGDebugInfo::EmitDeclare(const VarDecl *VD,
   }
   SmallVector<uint64_t, 13> Expr;
   llvm::DINode::DIFlags Flags = llvm::DINode::FlagZero;
-  if (VarIsArtificial)
+
+  // While synthesized Objective-C property setters are "artificial" (i.e., they
+  // are not spelled out in source), we want to pretend they are just like a
+  // regular non-compiler generated method. Hence, don't mark explicitly passed
+  // parameters of such methods as artificial.
+  if (VarIsArtificial && !IsObjCSynthesizedPropertyExplicitParameter(VD))
     Flags |= llvm::DINode::FlagArtificial;
 
   auto Align = getDeclAlignIfRequired(VD, CGM.getContext());
diff --git a/clang/test/DebugInfo/ObjC/property-synthesized-accessors.m b/clang/test/DebugInfo/ObjC/property-synthesized-accessors.m
new file mode 100644
index 0000000000000..6ed2885024c48
--- /dev/null
+++ b/clang/test/DebugInfo/ObjC/property-synthesized-accessors.m
@@ -0,0 +1,63 @@
+// Test that synthesized accessors get treated like regular method declarations/defeinitions.
+// I.e.:
+// 1. explicitly passed parameter are not marked artificial.
+// 2. Each property accessor has a method declaration and definition.
+
+// RUN: %clang_cc1 -triple %itanium_abi_triple -emit-llvm -dwarf-version=5 -debug-info-kind=limited %s -o - | FileCheck %s --implicit-check-not "DIFlagArtificial"
+
+@interface Foo
+@property int p1;
+@end
+
+@implementation Foo
+@end
+
+int main(void) {
+  Foo *f;
+  f.p1 = 2;
+  return f.p1;
+}
+
+// CHECK: ![[P1_TYPE:[0-9]+]] = !DIBasicType(name: "int"
+// CHECK: ![[GETTER_DECL:[0-9]+]] = !DISubprogram(name: "-[Foo p1]"
+// CHECK-SAME:                                    type: ![[GETTER_TYPE:[0-9]+]]
+// CHECK-SAME:                                    flags: DIFlagArtificial | DIFlagPrototyped
+// CHECK-SAME:                                    spFlags: DISPFlagLocalToUnit)
+
+// CHECK: ![[GETTER_TYPE]] = !DISubroutineType(types: ![[GETTER_PARAMS:[0-9]+]])
+// CHECK: ![[GETTER_PARAMS]] = !{![[P1_TYPE]], ![[ID_TYPE:[0-9]+]], ![[SEL_TYPE:[0-9]+]]}
+// CHECK: ![[ID_TYPE]] = !DIDerivedType(tag: DW_TAG_pointer_type
+// CHECK-SAME:                          flags: DIFlagArtificial | DIFlagObjectPointer)
+// CHECK: ![[SEL_TYPE]] = !DIDerivedType(tag: DW_TAG_typedef, name: "SEL"
+// CHECK-SAME:                           flags: DIFlagArtificial)
+
+// CHECK: ![[SETTER_DECL:[0-9]+]] = !DISubprogram(name: "-[Foo setP1:]"
+// CHECK-SAME:                                    type: ![[SETTER_TYPE:[0-9]+]]
+// CHECK-SAME:                                    flags: DIFlagArtificial | DIFlagPrototyped
+// CHECK-SAME:                                    spFlags: DISPFlagLocalToUnit)
+// CHECK: ![[SETTER_TYPE]] = !DISubroutineType(types: ![[SETTER_PARAMS:[0-9]+]])
+// CHECK: ![[SETTER_PARAMS]] = !{null, ![[ID_TYPE]], ![[SEL_TYPE]], ![[P1_TYPE]]}
+
+// CHECK: ![[GETTER_DEF:[0-9]+]] = distinct !DISubprogram(name: "-[Foo p1]"
+// CHECK-SAME:                                            type: ![[GETTER_TYPE]]
+// CHECK-SAME:                                            flags: DIFlagArtificial | DIFlagPrototyped
+// CHECK-SAME:                                            spFlags: DISPFlagLocalToUnit | DISPFlagDefinition
+// CHECK-SAME:                                            declaration: ![[GETTER_DECL]]
+
+// CHECK: !DILocalVariable(name: "self", arg: 1, scope: ![[GETTER_DEF]]
+// CHECK-SAME:             flags: DIFlagArtificial | DIFlagObjectPointer)
+//
+// CHECK: !DILocalVariable(name: "_cmd", arg: 2, scope: ![[GETTER_DEF]],
+// CHECK-SAME:             flags: DIFlagArtificial)
+
+// CHECK: ![[SETTER_DEF:[0-9]+]] = distinct !DISubprogram(name: "-[Foo setP1:]",
+// CHECK-SAME:                                            type: ![[SETTER_TYPE]]
+// CHECK-SAME:                                            flags: DIFlagArtificial | DIFlagPrototyped
+// CHECK-SAME:                                            spFlags: DISPFlagLocalToUnit | DISPFlagDefinition
+// CHECK-SAME:                                            declaration: ![[SETTER_DECL]]
+
+// CHECK: !DILocalVariable(name: "self", arg: 1, scope: ![[SETTER_DEF]]
+// CHECK-SAME:             flags: DIFlagArtificial | DIFlagObjectPointer
+// CHECK: !DILocalVariable(name: "_cmd", arg: 2, scope: ![[SETTER_DEF]]
+// CHECK-SAME:             flags: DIFlagArtificial
+// CHECK: !DILocalVariable(name: "p1", arg: 3, scope: ![[SETTER_DEF]]

@llvmbot
Copy link
Member

llvmbot commented Oct 24, 2025

@llvm/pr-subscribers-clang

Author: Michael Buch (Michael137)

Changes

In the past we used to only mark variables artificial that were isImplicit. We would also omit the location information if the variable's parent was implicit. Since #100355 we made the logic to mark variables as artificial the same as determining whether to omit its location or not. This was to support binding variable declarations, which we would like to have line information for (and don't want to mark artificial as they are explicitly named in source).

However, this doesn't quite do the expected for parameters of Objective-C synthesised property accessors. An Objective-C setter will have an explicit parameter, which is the ivar to write to. However, because the parent (i.e., the synthesised method) is artificial, we now mark that parameter artificial. This is example debug-info for such an accessor:

0x00000118:   DW_TAG_subprogram                                                                           
                DW_AT_low_pc    (0x0000000000000044)
                DW_AT_high_pc   (0x0000000000000078)
                DW_AT_frame_base        (DW_OP_reg29 W29)   
                DW_AT_object_pointer    (0x00000128)
                DW_AT_specification     (0x00000068 "-[Foo setFooProp:]")
                                                     
0x00000128:     DW_TAG_formal_parameter
                  DW_AT_location        (DW_OP_fbreg -8)
                  DW_AT_name    ("self")                                                                  
                  DW_AT_type    (0x00000186 "Foo *")
                  DW_AT_artificial      (true)    
                                                     
0x00000131:     DW_TAG_formal_parameter
                  DW_AT_location        (DW_OP_breg31 WSP+16)
                  DW_AT_name    ("_cmd")
                  DW_AT_type    (0x0000018b "SEL")
                  DW_AT_artificial      (true)      
                                                     
0x0000013a:     DW_TAG_formal_parameter                                                                   
                  DW_AT_location        (DW_OP_breg31 WSP+8)
                  DW_AT_name    ("fooProp")                                                               
                  DW_AT_type    (0x000000aa "id")
                  DW_AT_artificial      (true)    

Note how the fooProp parameter is marked artificial, although it really isn't. We want to treat the synthesised method like any other, where explicitly passed parameters aren't artificial.

This patch prevents such parameters from being marked artificial. We could probably generalise this to any kind of synthesised method, not just Objective-C. But I'm currently not aware of such synthesised functions, so made it Objective-C specific for now for testability.

rdar://163063569


Full diff: https://github.com/llvm/llvm-project/pull/164998.diff

2 Files Affected:

  • (modified) clang/lib/CodeGen/CGDebugInfo.cpp (+33-1)
  • (added) clang/test/DebugInfo/ObjC/property-synthesized-accessors.m (+63)
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 12e2813ef2ec7..6af806686a3b9 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -110,6 +110,33 @@ static bool IsArtificial(VarDecl const *VD) {
                               cast<Decl>(VD->getDeclContext())->isImplicit());
 }
 
+/// Returns \c true if the specified variable \c VD is an explicit parameter of
+/// a synthesized Objective-C property accessor. E.g., a synthesized property
+/// setter method will have a single explicit parameter which is the property to
+/// set.
+static bool IsObjCSynthesizedPropertyExplicitParameter(VarDecl const *VD) {
+  assert(VD);
+
+  if (!llvm::isa<ParmVarDecl>(VD))
+    return false;
+
+  // Not a property method.
+  const auto *Method =
+      llvm::dyn_cast_or_null<ObjCMethodDecl>(VD->getDeclContext());
+  if (!Method)
+    return false;
+
+  // Not a synthesized property accessor.
+  if (!Method->isImplicit() || !Method->isPropertyAccessor())
+    return false;
+
+  // Not an explicit parameter.
+  if (VD->isImplicit())
+    return false;
+
+  return true;
+}
+
 CGDebugInfo::CGDebugInfo(CodeGenModule &CGM)
     : CGM(CGM), DebugKind(CGM.getCodeGenOpts().getDebugInfo()),
       DebugTypeExtRefs(CGM.getCodeGenOpts().DebugTypeExtRefs),
@@ -5158,7 +5185,12 @@ llvm::DILocalVariable *CGDebugInfo::EmitDeclare(const VarDecl *VD,
   }
   SmallVector<uint64_t, 13> Expr;
   llvm::DINode::DIFlags Flags = llvm::DINode::FlagZero;
-  if (VarIsArtificial)
+
+  // While synthesized Objective-C property setters are "artificial" (i.e., they
+  // are not spelled out in source), we want to pretend they are just like a
+  // regular non-compiler generated method. Hence, don't mark explicitly passed
+  // parameters of such methods as artificial.
+  if (VarIsArtificial && !IsObjCSynthesizedPropertyExplicitParameter(VD))
     Flags |= llvm::DINode::FlagArtificial;
 
   auto Align = getDeclAlignIfRequired(VD, CGM.getContext());
diff --git a/clang/test/DebugInfo/ObjC/property-synthesized-accessors.m b/clang/test/DebugInfo/ObjC/property-synthesized-accessors.m
new file mode 100644
index 0000000000000..6ed2885024c48
--- /dev/null
+++ b/clang/test/DebugInfo/ObjC/property-synthesized-accessors.m
@@ -0,0 +1,63 @@
+// Test that synthesized accessors get treated like regular method declarations/defeinitions.
+// I.e.:
+// 1. explicitly passed parameter are not marked artificial.
+// 2. Each property accessor has a method declaration and definition.
+
+// RUN: %clang_cc1 -triple %itanium_abi_triple -emit-llvm -dwarf-version=5 -debug-info-kind=limited %s -o - | FileCheck %s --implicit-check-not "DIFlagArtificial"
+
+@interface Foo
+@property int p1;
+@end
+
+@implementation Foo
+@end
+
+int main(void) {
+  Foo *f;
+  f.p1 = 2;
+  return f.p1;
+}
+
+// CHECK: ![[P1_TYPE:[0-9]+]] = !DIBasicType(name: "int"
+// CHECK: ![[GETTER_DECL:[0-9]+]] = !DISubprogram(name: "-[Foo p1]"
+// CHECK-SAME:                                    type: ![[GETTER_TYPE:[0-9]+]]
+// CHECK-SAME:                                    flags: DIFlagArtificial | DIFlagPrototyped
+// CHECK-SAME:                                    spFlags: DISPFlagLocalToUnit)
+
+// CHECK: ![[GETTER_TYPE]] = !DISubroutineType(types: ![[GETTER_PARAMS:[0-9]+]])
+// CHECK: ![[GETTER_PARAMS]] = !{![[P1_TYPE]], ![[ID_TYPE:[0-9]+]], ![[SEL_TYPE:[0-9]+]]}
+// CHECK: ![[ID_TYPE]] = !DIDerivedType(tag: DW_TAG_pointer_type
+// CHECK-SAME:                          flags: DIFlagArtificial | DIFlagObjectPointer)
+// CHECK: ![[SEL_TYPE]] = !DIDerivedType(tag: DW_TAG_typedef, name: "SEL"
+// CHECK-SAME:                           flags: DIFlagArtificial)
+
+// CHECK: ![[SETTER_DECL:[0-9]+]] = !DISubprogram(name: "-[Foo setP1:]"
+// CHECK-SAME:                                    type: ![[SETTER_TYPE:[0-9]+]]
+// CHECK-SAME:                                    flags: DIFlagArtificial | DIFlagPrototyped
+// CHECK-SAME:                                    spFlags: DISPFlagLocalToUnit)
+// CHECK: ![[SETTER_TYPE]] = !DISubroutineType(types: ![[SETTER_PARAMS:[0-9]+]])
+// CHECK: ![[SETTER_PARAMS]] = !{null, ![[ID_TYPE]], ![[SEL_TYPE]], ![[P1_TYPE]]}
+
+// CHECK: ![[GETTER_DEF:[0-9]+]] = distinct !DISubprogram(name: "-[Foo p1]"
+// CHECK-SAME:                                            type: ![[GETTER_TYPE]]
+// CHECK-SAME:                                            flags: DIFlagArtificial | DIFlagPrototyped
+// CHECK-SAME:                                            spFlags: DISPFlagLocalToUnit | DISPFlagDefinition
+// CHECK-SAME:                                            declaration: ![[GETTER_DECL]]
+
+// CHECK: !DILocalVariable(name: "self", arg: 1, scope: ![[GETTER_DEF]]
+// CHECK-SAME:             flags: DIFlagArtificial | DIFlagObjectPointer)
+//
+// CHECK: !DILocalVariable(name: "_cmd", arg: 2, scope: ![[GETTER_DEF]],
+// CHECK-SAME:             flags: DIFlagArtificial)
+
+// CHECK: ![[SETTER_DEF:[0-9]+]] = distinct !DISubprogram(name: "-[Foo setP1:]",
+// CHECK-SAME:                                            type: ![[SETTER_TYPE]]
+// CHECK-SAME:                                            flags: DIFlagArtificial | DIFlagPrototyped
+// CHECK-SAME:                                            spFlags: DISPFlagLocalToUnit | DISPFlagDefinition
+// CHECK-SAME:                                            declaration: ![[SETTER_DECL]]
+
+// CHECK: !DILocalVariable(name: "self", arg: 1, scope: ![[SETTER_DEF]]
+// CHECK-SAME:             flags: DIFlagArtificial | DIFlagObjectPointer
+// CHECK: !DILocalVariable(name: "_cmd", arg: 2, scope: ![[SETTER_DEF]]
+// CHECK-SAME:             flags: DIFlagArtificial
+// CHECK: !DILocalVariable(name: "p1", arg: 3, scope: ![[SETTER_DEF]]

Copy link
Collaborator

@adrian-prantl adrian-prantl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with typo inside

@@ -0,0 +1,63 @@
// Test that synthesized accessors get treated like regular method declarations/defeinitions.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Test that synthesized accessors get treated like regular method declarations/defeinitions.
// Test that synthesized accessors get treated like regular method declarations/definitions.

Michael137 added a commit to swiftlang/llvm-project that referenced this pull request Oct 25, 2025
…C property accessors artificial

In the past we used to only mark variables artificial that were `isImplicit`. We would also omit the location information if the variable's parent was implicit. Since llvm#100355 we made the logic to mark variables as artificial the same as determining whether to omit its location or not. This was to support binding variable declarations, which we would like to have line information for (and don't want to mark artificial as they are explicitly named in source).

However, this doesn't quite do the expected for parameters of Objective-C synthesised property accessors. An Objective-C setter will have an explicit parameter, which is the ivar to write to. However, because the parent (i.e., the synthesised method) is artificial, we now mark that parameter artificial. This is example debug-info for such an accessor:
```
0x00000118:   DW_TAG_subprogram
                DW_AT_low_pc    (0x0000000000000044)
                DW_AT_high_pc   (0x0000000000000078)
                DW_AT_frame_base        (DW_OP_reg29 W29)
                DW_AT_object_pointer    (0x00000128)
                DW_AT_specification     (0x00000068 "-[Foo setFooProp:]")

0x00000128:     DW_TAG_formal_parameter
                  DW_AT_location        (DW_OP_fbreg -8)
                  DW_AT_name    ("self")
                  DW_AT_type    (0x00000186 "Foo *")
                  DW_AT_artificial      (true)

0x00000131:     DW_TAG_formal_parameter
                  DW_AT_location        (DW_OP_breg31 WSP+16)
                  DW_AT_name    ("_cmd")
                  DW_AT_type    (0x0000018b "SEL")
                  DW_AT_artificial      (true)

0x0000013a:     DW_TAG_formal_parameter
                  DW_AT_location        (DW_OP_breg31 WSP+8)
                  DW_AT_name    ("fooProp")
                  DW_AT_type    (0x000000aa "id")
                  DW_AT_artificial      (true)
```

Note how the `fooProp` parameter is marked artificial, although it technically is an explicitly passed parameter. We want to treat the synthesised method like any other, where explicitly passed parameters aren't artificial. But we do want to omit the file/line info because it doesn't exist in the source.

This patch prevents such parameters from being marked artificial. We could probably generalise this to any kind of synthesised method, not just Objective-C. But I'm currently not aware of such synthesised functions, so made it Objective-C specific for now for testability.

*Motivator*
Marking such parameters artificial makes LLDB fail to parse the ObjC method and emit an error such as:
```
error: Foo.o [0x00000000000009d7]: invalid Objective-C method DW_TAG_subprogram (DW_TAG_subprogram), please file a bug and attach the file at the start of this error message
```

rdar://163063569

(cherry-picked from llvm#164998)
@Michael137 Michael137 merged commit f8b004d into llvm:main Oct 27, 2025
10 checks passed
@Michael137 Michael137 deleted the clang/property-artificial-param branch October 27, 2025 07:49
Michael137 added a commit that referenced this pull request Oct 27, 2025
Prior to #164998, recent LLDB
versions would fail to parse synthesized property setters correctly. The
only way this failure would manifest is an error to the console:
```
error: main.o [0x00000000000000cd]: invalid Objective-C method DW_TAG_subprogram (DW_TAG_subprogram), please file a bug and attach the file at the start of this error message
```

There weren't any Objective-C tests that failed when the original regression (#100355) landed. This patch adds a test that explicitly checks that the type of the setter is sensible.

This test fails without #164998
and passes with it.

I decided not to check for the absence of the console error because that kind of test would be fragile to the removal of (or any changes to) the error message.
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Oct 27, 2025
…properties

Prior to llvm/llvm-project#164998, recent LLDB
versions would fail to parse synthesized property setters correctly. The
only way this failure would manifest is an error to the console:
```
error: main.o [0x00000000000000cd]: invalid Objective-C method DW_TAG_subprogram (DW_TAG_subprogram), please file a bug and attach the file at the start of this error message
```

There weren't any Objective-C tests that failed when the original regression (llvm/llvm-project#100355) landed. This patch adds a test that explicitly checks that the type of the setter is sensible.

This test fails without llvm/llvm-project#164998
and passes with it.

I decided not to check for the absence of the console error because that kind of test would be fragile to the removal of (or any changes to) the error message.
Michael137 added a commit to Michael137/llvm-project that referenced this pull request Oct 27, 2025
Prior to llvm#164998, recent LLDB
versions would fail to parse synthesized property setters correctly. The
only way this failure would manifest is an error to the console:
```
error: main.o [0x00000000000000cd]: invalid Objective-C method DW_TAG_subprogram (DW_TAG_subprogram), please file a bug and attach the file at the start of this error message
```

There weren't any Objective-C tests that failed when the original regression (llvm#100355) landed. This patch adds a test that explicitly checks that the type of the setter is sensible.

This test fails without llvm#164998
and passes with it.

I decided not to check for the absence of the console error because that kind of test would be fragile to the removal of (or any changes to) the error message.
dvbuka pushed a commit to dvbuka/llvm-project that referenced this pull request Oct 27, 2025
…property accessors artificial (llvm#164998)

In the past we used to only mark variables artificial that were
`isImplicit`. We would also omit the location information if the
variable's parent was implicit. Since
llvm#100355 we made the logic to
mark variables as artificial the same as determining whether to omit its
location or not. This was to support binding variable declarations,
which we would like to have line information for (and don't want to mark
artificial as they are explicitly named in source).

However, this doesn't quite do the expected for parameters of
Objective-C synthesised property accessors. An Objective-C setter will
have an explicit parameter, which is the ivar to write to. However,
because the parent (i.e., the synthesised method) is artificial, we now
mark that parameter artificial. This is example debug-info for such an
accessor:
```
0x00000118:   DW_TAG_subprogram                                                                           
                DW_AT_low_pc    (0x0000000000000044)
                DW_AT_high_pc   (0x0000000000000078)
                DW_AT_frame_base        (DW_OP_reg29 W29)   
                DW_AT_object_pointer    (0x00000128)
                DW_AT_specification     (0x00000068 "-[Foo setFooProp:]")
                                                     
0x00000128:     DW_TAG_formal_parameter
                  DW_AT_location        (DW_OP_fbreg -8)
                  DW_AT_name    ("self")                                                                  
                  DW_AT_type    (0x00000186 "Foo *")
                  DW_AT_artificial      (true)    
                                                     
0x00000131:     DW_TAG_formal_parameter
                  DW_AT_location        (DW_OP_breg31 WSP+16)
                  DW_AT_name    ("_cmd")
                  DW_AT_type    (0x0000018b "SEL")
                  DW_AT_artificial      (true)      
                                                     
0x0000013a:     DW_TAG_formal_parameter                                                                   
                  DW_AT_location        (DW_OP_breg31 WSP+8)
                  DW_AT_name    ("fooProp")                                                               
                  DW_AT_type    (0x000000aa "id")
                  DW_AT_artificial      (true)    
```

Note how the `fooProp` parameter is marked artificial, although it
technically is an explicitly passed parameter. We want to treat the
synthesised method like any other, where explicitly passed parameters
aren't artificial. But we do want to omit the file/line info because it
doesn't exist in the source.

This patch prevents such parameters from being marked artificial. We
could probably generalise this to any kind of synthesised method, not
just Objective-C. But I'm currently not aware of such synthesised
functions, so made it Objective-C specific for now for testability.

*Motivator*
Marking such parameters artificial makes LLDB fail to parse the ObjC
method and emit an error such as:
```
error: Foo.o [0x00000000000009d7]: invalid Objective-C method DW_TAG_subprogram (DW_TAG_subprogram), please file a bug and attach the file at the start of this error message
```

rdar://163063569
dvbuka pushed a commit to dvbuka/llvm-project that referenced this pull request Oct 27, 2025
Prior to llvm#164998, recent LLDB
versions would fail to parse synthesized property setters correctly. The
only way this failure would manifest is an error to the console:
```
error: main.o [0x00000000000000cd]: invalid Objective-C method DW_TAG_subprogram (DW_TAG_subprogram), please file a bug and attach the file at the start of this error message
```

There weren't any Objective-C tests that failed when the original regression (llvm#100355) landed. This patch adds a test that explicitly checks that the type of the setter is sensible.

This test fails without llvm#164998
and passes with it.

I decided not to check for the absence of the console error because that kind of test would be fragile to the removal of (or any changes to) the error message.
Lukacma pushed a commit to Lukacma/llvm-project that referenced this pull request Oct 29, 2025
…property accessors artificial (llvm#164998)

In the past we used to only mark variables artificial that were
`isImplicit`. We would also omit the location information if the
variable's parent was implicit. Since
llvm#100355 we made the logic to
mark variables as artificial the same as determining whether to omit its
location or not. This was to support binding variable declarations,
which we would like to have line information for (and don't want to mark
artificial as they are explicitly named in source).

However, this doesn't quite do the expected for parameters of
Objective-C synthesised property accessors. An Objective-C setter will
have an explicit parameter, which is the ivar to write to. However,
because the parent (i.e., the synthesised method) is artificial, we now
mark that parameter artificial. This is example debug-info for such an
accessor:
```
0x00000118:   DW_TAG_subprogram                                                                           
                DW_AT_low_pc    (0x0000000000000044)
                DW_AT_high_pc   (0x0000000000000078)
                DW_AT_frame_base        (DW_OP_reg29 W29)   
                DW_AT_object_pointer    (0x00000128)
                DW_AT_specification     (0x00000068 "-[Foo setFooProp:]")
                                                     
0x00000128:     DW_TAG_formal_parameter
                  DW_AT_location        (DW_OP_fbreg -8)
                  DW_AT_name    ("self")                                                                  
                  DW_AT_type    (0x00000186 "Foo *")
                  DW_AT_artificial      (true)    
                                                     
0x00000131:     DW_TAG_formal_parameter
                  DW_AT_location        (DW_OP_breg31 WSP+16)
                  DW_AT_name    ("_cmd")
                  DW_AT_type    (0x0000018b "SEL")
                  DW_AT_artificial      (true)      
                                                     
0x0000013a:     DW_TAG_formal_parameter                                                                   
                  DW_AT_location        (DW_OP_breg31 WSP+8)
                  DW_AT_name    ("fooProp")                                                               
                  DW_AT_type    (0x000000aa "id")
                  DW_AT_artificial      (true)    
```

Note how the `fooProp` parameter is marked artificial, although it
technically is an explicitly passed parameter. We want to treat the
synthesised method like any other, where explicitly passed parameters
aren't artificial. But we do want to omit the file/line info because it
doesn't exist in the source.

This patch prevents such parameters from being marked artificial. We
could probably generalise this to any kind of synthesised method, not
just Objective-C. But I'm currently not aware of such synthesised
functions, so made it Objective-C specific for now for testability.

*Motivator*
Marking such parameters artificial makes LLDB fail to parse the ObjC
method and emit an error such as:
```
error: Foo.o [0x00000000000009d7]: invalid Objective-C method DW_TAG_subprogram (DW_TAG_subprogram), please file a bug and attach the file at the start of this error message
```

rdar://163063569
Lukacma pushed a commit to Lukacma/llvm-project that referenced this pull request Oct 29, 2025
Prior to llvm#164998, recent LLDB
versions would fail to parse synthesized property setters correctly. The
only way this failure would manifest is an error to the console:
```
error: main.o [0x00000000000000cd]: invalid Objective-C method DW_TAG_subprogram (DW_TAG_subprogram), please file a bug and attach the file at the start of this error message
```

There weren't any Objective-C tests that failed when the original regression (llvm#100355) landed. This patch adds a test that explicitly checks that the type of the setter is sensible.

This test fails without llvm#164998
and passes with it.

I decided not to check for the absence of the console error because that kind of test would be fragile to the removal of (or any changes to) the error message.
aokblast pushed a commit to aokblast/llvm-project that referenced this pull request Oct 30, 2025
…property accessors artificial (llvm#164998)

In the past we used to only mark variables artificial that were
`isImplicit`. We would also omit the location information if the
variable's parent was implicit. Since
llvm#100355 we made the logic to
mark variables as artificial the same as determining whether to omit its
location or not. This was to support binding variable declarations,
which we would like to have line information for (and don't want to mark
artificial as they are explicitly named in source).

However, this doesn't quite do the expected for parameters of
Objective-C synthesised property accessors. An Objective-C setter will
have an explicit parameter, which is the ivar to write to. However,
because the parent (i.e., the synthesised method) is artificial, we now
mark that parameter artificial. This is example debug-info for such an
accessor:
```
0x00000118:   DW_TAG_subprogram                                                                           
                DW_AT_low_pc    (0x0000000000000044)
                DW_AT_high_pc   (0x0000000000000078)
                DW_AT_frame_base        (DW_OP_reg29 W29)   
                DW_AT_object_pointer    (0x00000128)
                DW_AT_specification     (0x00000068 "-[Foo setFooProp:]")
                                                     
0x00000128:     DW_TAG_formal_parameter
                  DW_AT_location        (DW_OP_fbreg -8)
                  DW_AT_name    ("self")                                                                  
                  DW_AT_type    (0x00000186 "Foo *")
                  DW_AT_artificial      (true)    
                                                     
0x00000131:     DW_TAG_formal_parameter
                  DW_AT_location        (DW_OP_breg31 WSP+16)
                  DW_AT_name    ("_cmd")
                  DW_AT_type    (0x0000018b "SEL")
                  DW_AT_artificial      (true)      
                                                     
0x0000013a:     DW_TAG_formal_parameter                                                                   
                  DW_AT_location        (DW_OP_breg31 WSP+8)
                  DW_AT_name    ("fooProp")                                                               
                  DW_AT_type    (0x000000aa "id")
                  DW_AT_artificial      (true)    
```

Note how the `fooProp` parameter is marked artificial, although it
technically is an explicitly passed parameter. We want to treat the
synthesised method like any other, where explicitly passed parameters
aren't artificial. But we do want to omit the file/line info because it
doesn't exist in the source.

This patch prevents such parameters from being marked artificial. We
could probably generalise this to any kind of synthesised method, not
just Objective-C. But I'm currently not aware of such synthesised
functions, so made it Objective-C specific for now for testability.

*Motivator*
Marking such parameters artificial makes LLDB fail to parse the ObjC
method and emit an error such as:
```
error: Foo.o [0x00000000000009d7]: invalid Objective-C method DW_TAG_subprogram (DW_TAG_subprogram), please file a bug and attach the file at the start of this error message
```

rdar://163063569
aokblast pushed a commit to aokblast/llvm-project that referenced this pull request Oct 30, 2025
Prior to llvm#164998, recent LLDB
versions would fail to parse synthesized property setters correctly. The
only way this failure would manifest is an error to the console:
```
error: main.o [0x00000000000000cd]: invalid Objective-C method DW_TAG_subprogram (DW_TAG_subprogram), please file a bug and attach the file at the start of this error message
```

There weren't any Objective-C tests that failed when the original regression (llvm#100355) landed. This patch adds a test that explicitly checks that the type of the setter is sensible.

This test fails without llvm#164998
and passes with it.

I decided not to check for the absence of the console error because that kind of test would be fragile to the removal of (or any changes to) the error message.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:codegen IR generation bugs: mangling, exceptions, etc. clang Clang issues not falling into any other category debuginfo

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants